package com.icw.configuration.web.interceptor.outest;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Stream;

import javax.servlet.http.HttpServletResponse;

import org.springframework.aop.Advisor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.icw.configuration.ConstSettings;
import com.icw.configuration.data_interchange.RpcResult;
import com.icw.utility.IcwUtils;
import com.icw.utility.PoEntityHelper;

@Configuration
public class WebApiDtoNormAspect {

	@Bean
	public Advisor webApiDataOut() throws Exception {
		return IcwUtils.cutExpression(invocation -> {
			Object[] args = invocation.getArguments();
			HttpServletResponse response = IcwUtils.findArgs(args, HttpServletResponse.class);
			@SuppressWarnings("unchecked")
			Map<String, Object> dataout = Map.class.isInstance(args[args.length - 1]) ? Map.class.cast(args[args.length - 1]) : null;
			return IcwUtils.tryAndCatch(() -> invocation.proceed(), (res, ex) -> {
				// 1. no response or output was called means default output
				if ((response == null || usingOutputstream(response)) && ex == null) return res;
				if ((response == null || usingOutputstream(response)) && ex != null) throw ex;
				// 2. has response means DIY output
				Object outputObj;
				/*--*/if (ex != null) outputObj = new RpcResult(ex);
				else if (res != null) outputObj = new RpcResult(IcwUtils.mapObject("data", res));
				/*-------------*/else outputObj = new RpcResult(removeRedundancy(dataout));
				IcwUtils.render(response, outputObj);
				return null;
			});
		}, ConstSettings.OUTEST_CUT_POINT, ConstSettings.FIRST_ORDER);
	}

	private Map<String, Object> removeRedundancy(Map<String, Object> data) {
		Stream// e
				.of(PoEntityHelper.keyOf(data))// e
				.filter(k -> k == null || k.indexOf("org.springframework.validation.BindingResult") == 0)// e
				.forEach(k -> data.remove(k));
		return new LinkedHashMap<String, Object>(data);
	}

	private boolean usingOutputstream(HttpServletResponse response) throws Exception {
		return (Boolean) PoEntityHelper.readDeclaredByProperty(response, "response.usingOutputStream");
	}
}